﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Text;
using System.Windows.Forms;
using StructureTree;
using HexControl;

//TODO: 안전성 고려
//TODO: ASCII 값에 선택 기능 추가
//TODO: Format Structure(XML) 파일을 C++ 문법에서 자동으로 변환해주는 도구

//TODO: 모든 옵션 설정을 Configuration file(config.xml)로 빼내기
//TODO: 참조값에 대해서는 선택시에 참조에 사용된 값들도 보여주면 어떨까?
//TODO: 나중에 버그를 쉽게 찾을 수 있도록 사용자가 한 모든 행위들을 기록하는 장치 추가
//TODO: 긴 파일 이름을 생략해서 보여주기
//TODO: HexControl에서 ToolTip 보여줄 때 시간 계산해서 보여주기

//TODO: 애드인
//TODO: Redo/Undo
//TODO: Hex-Control 크기에 따른 자동 확장
//TODO: 횡 스크롤 지원(Hex Control)
//TODO: Export 기능(Structure에 맞게 데이터 저장, 16진수 또는 10진수)
//TODO: XML 확장 기능 정리
//TODO: 인쇄 기능
//TODO: Copy 기능
//TODO: Event-Driven 구조 정리
//TODO: 옵션 기능 고려


//TODO: (완료) 선택 블록 좀더 매끄럽게 보여주기 (widen)으로 해결 (Draw outline)
//TODO: (완료) 상대 주소 값 계산할 때 사칙 연산이 가능하도록 기능 확장 (Calculate relative variables)
//TODO: (완료) PE 헤더에 대한 파일 구조 완성(XML 문서 기능 확장)
//TODO: (완료) XML 문서에서 주석은 무시
//TODO: (완료) HEX에서 값을 선택하면 그게 역으로 XML node에 적용될 수 있도록(역선택 기능) 한다.
//TODO: (완료) HEX 값 편집 기능


namespace FormatVisualizer
{
    public partial class MainForm : Form, ITreeEventHandler, IHexControlEventHandler
    {
        private Boolean _LoadedBinaryFile;
        private String _BinaryFileName;
        private String _XmlFileName;
        private int _BinaryFileSize;

        protected bool LoadedBinaryFile
        {
            get { return _LoadedBinaryFile; }
            set { _LoadedBinaryFile = value; }
        }
        protected string BinaryFileName
        {
            get { return _BinaryFileName; }
            set { _BinaryFileName = value; }
        }
        protected string XmlFileName
        {
            get { return _XmlFileName; }
            set { _XmlFileName = value; }
        }
        protected int BinaryFileSize
        {
            get { return _BinaryFileSize; }
            set { _BinaryFileSize = value; }
        }

        public MainForm()
        {
            InitializeComponent();

            structureTree.SetEventHandler(this);
            hexControl.SetEventHandler(this);

            this.LoadedBinaryFile = false;
        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Stream openStream = null;

            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Filter = "All files (*.*)|*.*|Executable files (*.exe)|*.exe|Image files (*.bmp, *.jpg, *.gif)|*.bmp;*.jpg;*.gif";
            openFileDialog.FilterIndex = 1;
            openFileDialog.RestoreDirectory = false;
            openFileDialog.AutoUpgradeEnabled = true;

            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    if ((openStream = openFileDialog.OpenFile()) != null)
                    {
                        using (openStream)
                        {
                            BinaryReader reader = new BinaryReader(openStream, Encoding.ASCII);
                            int size = (int) openStream.Seek(0, SeekOrigin.End);
                            _BinaryFileSize = size;

                            openStream.Seek(0, SeekOrigin.Begin);
                            byte[] data = new byte[size];
                            reader.Read(data, 0, size);

                            hexControl.SetStream(data, size);

                            LoadedBinaryFile = true;
                            BinaryFileName = openFileDialog.FileName;
                            data = null;
                        }

                        labelBinaryFile.Text = openFileDialog.FileName;
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
                }
            }

        }

        private void loadXmlToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (LoadedBinaryFile == false) {
                MessageBox.Show("Error: Could not load XML file because a Binary file was not loaded");
                return;
            }

            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Filter = "XML file(*.xml)|*.xml|All files (*.*)|*.*";
            openFileDialog.FilterIndex = 1;
            openFileDialog.RestoreDirectory = false;
            openFileDialog.AutoUpgradeEnabled = true;

            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    if (structureTree.LoadXmlFile(openFileDialog.FileName) == true) {
                        XmlFileName = openFileDialog.FileName;
                        labelXmlFile.Text = openFileDialog.FileName;
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Could not read file from disk. Original error: " + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }

            }
        }

        #region ITreeEventHandler Members

        public void SelectItem(FormatNode fnode)
        {
            if (LoadedBinaryFile == true )
            {
                hexControl.SelectRegion(fnode.Path, fnode.Text, fnode.Comment, fnode.Offset, fnode.Size);
            }
            propertyNodeGrid.SelectedObject = fnode;
        }

        public byte[] GetValue(int offset, int size)
        {
            if (offset + size > BinaryFileSize) {
                return null;
            }

            byte[] retBytes = new byte[size];

            for (int i = 0; i < size; i++ )
            {
                retBytes[i] = hexControl.GetByte(i + offset );
            }

            return retBytes;
        }

        #endregion

        #region IHexControlEventHandler Members

        public void ClickedOffset(int offset)
        {
            structureTree.SelectItemByOffset(offset);
        }

        #endregion
    }
}
